package gateway.config.security.login;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import common.config.http.HttpStatusEnum;
import common.config.rocketmq.MqTaskEnum;
import common.config.rocketmq.producer.SystemLogMqProducer;
import common.model.po.SysLog;
import common.model.response.base.Result;
import common.util.IpUtils;
import common.util.MultiReadHttpServletRequest;
import common.util.ResponseUtil;
import gateway.config.security.util.StopWatchUtil;
import gateway.model.request.LoginRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * 自定义认证失败处理方式
 *
 * @author 米泽鹏
 * @since 2021-07-31 4:59 下午
 */
@Slf4j
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

	@Resource
	private SystemLogMqProducer systemLogMqProducer;
	@Value("${spring.application.name}")
	private String appName;

	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) {
		SysLog sysLog = new SysLog();
		try {
			Result<?> result;
			HttpStatusEnum httpStatusEnum = HttpStatusEnum.getStatus(request);
			int statusCode = httpStatusEnum == null ? Result.DEFAULT_ERROR_CODE : httpStatusEnum.getCode();
			// 获取登录人
			MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
			LoginRequest loginRequest = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest), LoginRequest.class);
			if (authenticationException instanceof UsernameNotFoundException || authenticationException instanceof BadCredentialsException) {
				result = Result.error(statusCode, ExceptionUtil.getSimpleMessage(authenticationException));
			} else if (authenticationException instanceof LockedException) {
				ReflectUtil.setFieldValue(authenticationException, "detailMessage", "账号被锁定");
				result = Result.error(statusCode, "账号被锁定");
				sysLog.setOperator(loginRequest.getUsername());
			} else if (authenticationException instanceof CredentialsExpiredException) {
				ReflectUtil.setFieldValue(authenticationException, "detailMessage", "证书过期");
				result = Result.error(statusCode, "证书过期");
				sysLog.setOperator(loginRequest.getUsername());
			} else if (authenticationException instanceof AccountExpiredException) {
				ReflectUtil.setFieldValue(authenticationException, "detailMessage", "账号过期");
				result = Result.error(statusCode, "账号过期");
				sysLog.setOperator(loginRequest.getUsername());
			} else if (authenticationException instanceof DisabledException) {
				ReflectUtil.setFieldValue(authenticationException, "detailMessage", "账号被禁用");
				result = Result.error(statusCode, "账号被禁用");
				sysLog.setOperator(loginRequest.getUsername());
			} else {
				log.error("登录失败", authenticationException);
				result = Result.error(statusCode, ExceptionUtil.getSimpleMessage(authenticationException));
				sysLog.setOperator(loginRequest.getUsername());
			}
			ResponseUtil.out(response, result);
			// 日志
			sysLog.setMethodName("login");
			sysLog.setMethodDescription("登录");
			sysLog.setRequestUrl(request.getRequestURI().replace(request.getContextPath(), ""));
			sysLog.setIp(IpUtils.getIpAddress(request));
			sysLog.setStatusCode(statusCode);
			sysLog.setMessage(ExceptionUtil.getSimpleMessage(authenticationException));
			sysLog.setExecutionDate(new Date());
			// 执行耗时
			Long loginStartTime = StopWatchUtil.LOGIN_START_TIME.get();
			sysLog.setExecutionTime(System.currentTimeMillis() - loginStartTime);
			// RocketMQ单向消息：记录系统日志
			Message msg = new Message(MqTaskEnum.SYSTEM_LOG.getTopic(), appName, JSONUtil.toJsonStr(sysLog).getBytes(RemotingHelper.DEFAULT_CHARSET));
			systemLogMqProducer.sendOneway(msg);
			log.info("已发送RocketMQ单向消息：系统日志，来自服务：" + appName);
		} catch (MQClientException | RemotingException | InterruptedException e) {
			log.error("发送RocketMQ单向消息失败：系统日志，来自服务：{}，日志详情：{}", appName, JSONUtil.toJsonPrettyStr(sysLog), e);
		} catch (Exception e) {
			log.error("CustomAuthenticationFailureHandler.onAuthenticationFailure()发生异常", e);
		} finally {
			StopWatchUtil.LOGIN_START_TIME.remove();
		}
	}

}
